#include "BicubicUniformPeriodicBSplineSurfaces.h"
#include "BicubicUniformPeriodicBSplinePatch.h"
#include <vector>
#include <math.h>

using namespace cagd;
using namespace std;

BicubicUniformPeridocBSplineSurface::BicubicUniformPeridocBSplineSurface(
        GLuint row_count,
        GLuint column_count,
        GLboolean is_closed_u_direction,
        GLboolean is_closed_v_direction, \
        GLuint u_div_point_count,
        GLuint v_div_point_count,
        GLenum usage_flag,
        Material &default_material):
        _usage_flage(usage_flag),
        _default_material(default_material),
        _is_closed_u_direction(is_closed_u_direction),
        _is_closed_v_direction(is_closed_v_direction),
        _u_div_point_count(u_div_point_count),
        _v_div_point_count(v_div_point_count)
{
    _control_net.ResizeRows(row_count);
    _control_net.ResizeColumns(column_count);
}

BicubicUniformPeridocBSplineSurface::BicubicUniformPeridocBSplineSurface(
        const BicubicUniformPeridocBSplineSurface &surface):
        _control_net(surface._control_net),
        _usage_flage(surface._usage_flage),
        _default_material(surface._default_material),
        _is_closed_u_direction(surface._is_closed_u_direction),
        _is_closed_v_direction(surface._is_closed_v_direction),
        _u_div_point_count(surface._u_div_point_count),
        _v_div_point_count(surface._v_div_point_count),
        _patches(surface._patches),
        _meshes(surface._meshes),
        _materials(surface._materials)
{}

BicubicUniformPeridocBSplineSurface& BicubicUniformPeridocBSplineSurface::operator =(
        const BicubicUniformPeridocBSplineSurface& rhs)
{
    _control_net = rhs._control_net;
    _usage_flage = rhs._usage_flage;
    _default_material = rhs._default_material;
    _materials = rhs._materials;
    _patches = rhs._patches;
    _meshes = rhs._meshes;
    _is_closed_u_direction = rhs._is_closed_u_direction;
    _is_closed_v_direction = rhs._is_closed_v_direction;
    _u_div_point_count = rhs._u_div_point_count;
    _v_div_point_count = rhs._v_div_point_count;

    return *this;
}

GLboolean BicubicUniformPeridocBSplineSurface::SetControlPoint(GLuint row, GLuint column,
                                                               const DCoordinate &p,
                                                               GLboolean apply_to_patch,
                                                               GLenum usage_flag)
{
    if (row >= _control_net.GetRowCount() || column >= _control_net.GetColumnCount())
        return false;

    for(std::vector<DCoordinate *>::iterator cit= same_control_points(row,column).begin();cit!= same_control_points(row,column).end();cit++){
        *(*cit)= p;
    }

    for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row,column).begin();nit< neighbor_patches(row,column).end();nit++){
        if (!nit->patch->SetControlPoint(nit->row,nit->column,p)){
            return false;
        }
        if (apply_to_patch){
            if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                return false;
            }
        }
    }

    /*if((_is_closed_u_direction) && (row<= 2)){
        _control_net.SetElement(_control_net.GetRowCount()-3+row, column, p);

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(_control_net.GetRowCount()-3+row,column).begin();nit< neighbor_patches(_control_net.GetRowCount()-3+row,column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,p)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }

    if((_is_closed_v_direction) && (column<= 2)){
        _control_net.SetElement(row, _control_net.GetColumnCount()-3+column, p);

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row,_control_net.GetColumnCount()-3+column).begin();nit< neighbor_patches(row,_control_net.GetColumnCount()-3+column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,p)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }

    if((_is_closed_u_direction) && (row>= _control_net.GetRowCount()-3)){
        _control_net.SetElement(row-_control_net.GetRowCount()+3, column, p);

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row-_control_net.GetRowCount()+3,column).begin();nit< neighbor_patches(row-_control_net.GetRowCount()+3,column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,p)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }

    if((_is_closed_v_direction) && (column<= _control_net.GetColumnCount()-3)){
        _control_net.SetElement(row, _control_net.GetColumnCount()-3+column, p);

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row,_control_net.GetColumnCount()-3+column).begin();nit< neighbor_patches(row,_control_net.GetColumnCount()-3+column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,p)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }*/

   /* GLuint nr_row = _control_net.GetRowCount() - 2 - (_is_closed_u_direction ? 3 : 0);
    GLuint nr_col = _control_net.GetColumnCount() - 2 - (_is_closed_v_direction ? 3 : 0);
    GLuint inner_i = 0, inner_j;
    for (GLint i = max((GLint)row - (GLint)2, (GLint)0); i < min((GLint)nr_row, (GLint)(row + 2)); i++)
    {
        inner_j = 0;
        for (GLint j = max((GLint)column - (GLint)2, (GLint)0); j < min((GLint)nr_col, (GLint)(column + 2)); j++)
        {
            GLuint k = i * nr_row + j;
            if (!_patches[k].SetControlPoint(inner_i, inner_j, p))
                return false;
            if (apply_to_patch)
                if (!BuildASinglePatch(k, _u_div_point_count, _v_div_point_count, usage_flag))
                    return false;
            inner_j++;
        }
        inner_i++;
    }*/

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::SetControlPoint(GLuint row, GLuint column,
                                                               GLdouble x, GLdouble y, GLdouble z,
                                                               GLboolean apply_to_patch,
                                                               GLenum usage_flag)
{
    if (row >= _control_net.GetRowCount() || column >= _control_net.GetColumnCount())
        return false;

    for(std::vector<DCoordinate *>::iterator cit= same_control_points(row,column).begin();cit!= same_control_points(row,column).end();cit++){
        (*(*cit)).x= x;
        (*(*cit)).y= y;
        (*(*cit)).z= z;
    }

    /*DCoordinate &ref = _control_net(row, column);
    ref.x = x;
    ref.y = y;
    ref.z = z;*/

    for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row,column).begin();nit< neighbor_patches(row,column).end();nit++){
        if (!nit->patch->SetControlPoint(nit->row,nit->column,x,y,z)){
            return false;
        }
        if (apply_to_patch){
            if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                return false;
            }
        }
    }

    /*if((_is_closed_u_direction) && (row<= 2)){
        DCoordinate &ref = _control_net(_control_net.GetRowCount()-3+row, column);
        ref.x = x;
        ref.y = y;
        ref.z = z;

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(_control_net.GetRowCount()-3+row,column).begin();nit< neighbor_patches(_control_net.GetRowCount()-3+row,column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,x,y,z)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }

    if((_is_closed_v_direction) && (column<= 2)){
        DCoordinate &ref = _control_net(row, _control_net.GetColumnCount()-3+column);
        ref.x = x;
        ref.y = y;
        ref.z = z;

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row,_control_net.GetColumnCount()-3+column).begin();nit< neighbor_patches(row,_control_net.GetColumnCount()-3+column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,x,y,z)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }

    if((_is_closed_u_direction) && (row>= _control_net.GetRowCount()-3)){
        DCoordinate &ref = _control_net(row-_control_net.GetRowCount()+3, column);
        ref.x = x;
        ref.y = y;
        ref.z = z;

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row-_control_net.GetRowCount()+3,column).begin();nit< neighbor_patches(row-_control_net.GetRowCount()+3,column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,x,y,z)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }for (GLuint i = 1; i < n - 2; i++){
        for (GLuint j = 1; j < m - 2; j++){

    if((_is_closed_v_direction) && (column<= _control_net.GetColumnCount()-3)){
        DCoordinate &ref = _control_net(row, _control_net.GetColumnCount()-3+column);
        ref.x = x;
        ref.y = y;
        ref.z = z;

        for(std::vector<NeighborPatch>::iterator nit= neighbor_patches(row,_control_net.GetColumnCount()-3+column).begin();nit< neighbor_patches(row,_control_net.GetColumnCount()-3+column).end();nit++){
            if (!nit->patch->SetControlPoint(nit->row,nit->column,x,y,z)){
                return false;
            }
            if (apply_to_patch){
                if (!BuildASinglePatch(nit->index, _u_div_point_count, _v_div_point_count, usage_flag)){
                    return false;
                }
            }
        }
    }*/

    /*GLuint nr_row = _control_net.GetRowCount() - 2 - (_is_closed_u_direction ? 3 : 0);
    GLuint nr_col = _control_net.GetColumnCount() - 2 - (_is_closed_v_direction ? 3 : 0);
    GLuint inner_i = 0, inner_j;
    for (GLint i = max((GLint)row - (GLint)2, (GLint)0); i < min((GLint)nr_row, (GLint)(row + 2)); i++)
    {
        inner_j = 0;
        for (GLint j = max((GLint)column - (GLint)2, (GLint)0); j < min((GLint)nr_col, (GLint)(column + 2)); j++)
        {
            GLuint k = i * nr_row + j;
            if (!_patches[k].SetControlPoint(inner_i, inner_j, x, y, z))
                return false;
            if (apply_to_patch)
                if (!BuildASinglePatch(k, _u_div_point_count, _v_div_point_count, usage_flag))
                    return false;
            inner_j++;
        }
        inner_i++;
    }*/

    return true;
}
GLboolean BicubicUniformPeridocBSplineSurface::SetControlPoint(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLboolean apply_to_patch, GLenum usage_flage)
{
    GLuint col = (_is_closed_v_direction ? _control_net.GetColumnCount() - 3 : _control_net.GetColumnCount());
    GLuint row = (_is_closed_u_direction ? _control_net.GetRowCount() - 3 : _control_net.GetRowCount());

    if (index >= col * row)
        return false;

    GLuint j = index % col;
    GLuint i = (index - j) / col;

    GLboolean res = SetControlPoint(i, j, x, y, z, apply_to_patch, usage_flage);

    return res;
}
GLboolean BicubicUniformPeridocBSplineSurface::SetControlPoint(GLuint index, const DCoordinate &p, GLboolean apply_to_patch, GLenum usage_flage)
{
    GLuint col = (_is_closed_v_direction ? _control_net.GetColumnCount() - 3 : _control_net.GetColumnCount());
    GLuint row = (_is_closed_u_direction ? _control_net.GetRowCount() - 3 : _control_net.GetRowCount());

    if (index >= col * row)
        return false;

    GLuint j = index % col;
    GLuint i = (index - j) / col;

    GLboolean res = SetControlPoint(i, j, p, apply_to_patch, usage_flage);

    return res;
}

GLboolean BicubicUniformPeridocBSplineSurface::GetControlPoint(GLuint row, GLuint column, DCoordinate &p) const
{
    if (row >= _control_net.GetRowCount() || column >= _control_net.GetColumnCount())
        return false;

    p = _control_net(row, column);

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::GetControlPoint(GLuint row, GLuint column, GLdouble &x, GLdouble &y, GLdouble &z) const
{
    if (row >= _control_net.GetRowCount() || column >= _control_net.GetColumnCount())
        return false;

    const DCoordinate& ref = _control_net(row, column);
    x = ref[0];
    y = ref[1];
    z = ref[2];

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::GetControlPoint(GLuint index, GLdouble &x, GLdouble &y, GLdouble &z) const
{
    GLuint col = (_is_closed_v_direction ? _control_net.GetColumnCount() - 3 : _control_net.GetColumnCount());
    GLuint row = (_is_closed_u_direction ? _control_net.GetRowCount() - 3 : _control_net.GetRowCount());

    if (index >= col * row)
        return false;

    GLuint j = index % col;
    GLuint i = (index - j) / col;

    GLboolean res = GetControlPoint(i, j, x, y, z);

    return res;
}

GLboolean BicubicUniformPeridocBSplineSurface::GetControlPoint(GLuint index, DCoordinate &p) const
{
    GLuint col = (_is_closed_v_direction ? _control_net.GetColumnCount() - 3 : _control_net.GetColumnCount());
    GLuint row = (_is_closed_u_direction ? _control_net.GetRowCount() - 3 : _control_net.GetRowCount());

    if (index >= col * row)
        return false;

    GLuint j = index % col;
    GLuint i = (index - j) / col;

    GLboolean res = GetControlPoint(i, j, p);

    return res;
}

GLboolean BicubicUniformPeridocBSplineSurface::SetMaterial(GLuint patch_index, const Material &m)
{
    if (patch_index >= _patches.size())
        return false;

    _materials[patch_index] = m;

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::BuildAllPatches(
        GLuint u_div_point_count,
        GLuint v_div_point_count,
        GLenum usage_flag)
{
    GLuint n = _control_net.GetRowCount();
    GLuint m = _control_net.GetColumnCount();
    GLuint patch_count = (n - 3) * (m - 3);

    _patches.resize(patch_count);
    _meshes.resize(patch_count);
    _materials.resize(patch_count);

    NeighborPatch neighbor_info;

    for (vector<Material>::iterator it = _materials.begin(); it != _materials.end(); it++)
        *it = _default_material;

    neighbor_patches.ResizeRows(n);
    neighbor_patches.ResizeColumns(m);

    same_control_points.ResizeRows(n);
    same_control_points.ResizeColumns(m);

    vector<TriangulatedMesh>::iterator mit = _meshes.begin();
    vector<BicubicUniformPeriodicBSplinePatch>::iterator pit = _patches.begin();
    int index= 0;
    for (GLuint i = 1; i < n - 2; i++){
        for (GLuint j = 1; j < m - 2; j++){
            for (GLuint ii = 0; ii < 4; ii++){
                for (GLuint jj = 0; jj < 4; jj++){
                    if (!pit->SetControlPoint(ii, jj, _control_net(i - 1 + ii, j - 1 + jj))){
                        return false;
                    }
                    neighbor_info.patch= &(*pit);
                    neighbor_info.row= ii;
                    neighbor_info.column= jj;
                    neighbor_info.index= index;
                    neighbor_patches(i - 1 + ii,j - 1 + jj).push_back(neighbor_info);
                    //halo szelen
                    if((_is_closed_u_direction) && ((i - 1 + ii)<= 2)){
                        neighbor_patches(n-3+(i - 1 + ii),j - 1 + jj).push_back(neighbor_info);
                    }
                    if((_is_closed_v_direction) && ((j - 1 + jj)<= 2)){
                        neighbor_patches(i - 1 + ii,m-3+(j - 1 + jj)).push_back(neighbor_info);
                    }
                    if((_is_closed_u_direction) && ((i - 1 + ii)>= n-3)){
                        neighbor_patches((i - 1 + ii)-n+3,j - 1 + jj).push_back(neighbor_info);
                    }
                    if((_is_closed_u_direction) && ((j - 1 + jj)>= m-3)){
                        neighbor_patches(i - 1 + ii,(j - 1 + jj)-m+3).push_back(neighbor_info);
                    }
                    //halo sarkaiban
                    if(((_is_closed_u_direction) && ((i - 1 + ii)<= 2)) && ((_is_closed_v_direction) && ((j - 1 + jj)<= 2))){
                        neighbor_patches(n-3+(i - 1 + ii),m-3+(j - 1 + jj)).push_back(neighbor_info);
                    }
                    if(((_is_closed_u_direction) && ((i - 1 + ii)<= 2)) && ((_is_closed_u_direction) && ((j - 1 + jj)>= m-3))){
                        neighbor_patches(n-3+(i - 1 + ii),(j - 1 + jj)-m+3).push_back(neighbor_info);
                    }
                    if(((_is_closed_u_direction) && ((i - 1 + ii)>= n-3)) && ((_is_closed_v_direction) && ((j - 1 + jj)<= 2))){
                        neighbor_patches((i - 1 + ii)-n+3,m-3+(j - 1 + jj)).push_back(neighbor_info);
                    }
                    if(((_is_closed_u_direction) && ((i - 1 + ii)>= n-3)) && ((_is_closed_u_direction) && ((j - 1 + jj)>= m-3))){
                        neighbor_patches((i - 1 + ii)-n+3,(j - 1 + jj)-m+3).push_back(neighbor_info);
                    }
                }
            }
            if (!pit->GenerateMesh(u_div_point_count, v_div_point_count, *mit))
                return false;
            if (!mit->UpdateVertexBufferObjects(usage_flag))
                return false;
            pit++, mit++;
            index++;
        }
    }

    for (GLuint i = 0; i < n; i++){
        for (GLuint j = 0; j < m; j++){
            DCoordinate *control_point= &_control_net(i,j);
            same_control_points(i,j).push_back(control_point);
            //halo szelen
            if((_is_closed_u_direction) && (i<= 2)){
                same_control_points(n-3+i,j).push_back(control_point);
            }
            if((_is_closed_v_direction) && (j<= 2)){
                same_control_points(i,m-3+j).push_back(control_point);
            }
            if((_is_closed_u_direction) && (i>= n-3)){
                same_control_points(i-n+3,j).push_back(control_point);
            }
            if((_is_closed_u_direction) && (j>= m-3)){
                same_control_points(i,j-m+3).push_back(control_point);
            }
            //halo sarkaiban
            if(((_is_closed_u_direction) && (i<= 2)) && ((_is_closed_v_direction) && (j<= 2))){
                same_control_points(n-3+i,m-3+j).push_back(control_point);
            }
            if(((_is_closed_u_direction) && (i<= 2)) && ((_is_closed_u_direction) && (j>= m-3))){
                same_control_points(n-3+i,j-m+3).push_back(control_point);
            }
            if(((_is_closed_u_direction) && (i>= n-3)) && ((_is_closed_v_direction) && (j<= 2))){
                same_control_points(i-n+3,m-3+j).push_back(control_point);
            }
            if(((_is_closed_u_direction) && (i>= n-3)) && ((_is_closed_u_direction) && (j>= m-3))){
                same_control_points(i-n+3,j-m+3).push_back(control_point);
            }
        }
    }

   /* for(int i= 0;i< neighbor_patches.GetRowCount();i++){
        for(int j= 0;j< neighbor_patches.GetColumnCount();j++){
            printf("N: %d\n",neighbor_patches(i,j).size());
            //for(vector<NeighborPatch>::iterator it= neighbor_patches(i,j).begin();it< neighbor_patches(i,j).end();it++){
                //printf("%d\n",it->index);
            //}
        }
    }*/

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::BuildASinglePatch(GLuint patch_index, GLuint u_div_point_count, GLuint v_div_point_count, GLenum usage_flag)
{
    if (patch_index >= _patches.size())
        return false;

    if (!_patches[patch_index].GenerateMesh(u_div_point_count, v_div_point_count, _meshes[patch_index]))
        return false;
    if (!_meshes[patch_index].UpdateVertexBufferObjects(usage_flag))
        return false;

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::Render(GLenum render_mode) const
{
    //vector<Material>::iterator mit = _materials.begin();
    for (vector<TriangulatedMesh>::const_iterator it = _meshes.begin(); it != _meshes.end(); it++)//, mit++)
    {
        //mit->Apply();
        _default_material.Apply();
        if (!it->Render(render_mode))
            return false;
    }
    return true;
}

GLvoid BicubicUniformPeridocBSplineSurface::DeleteVBOOfControlNet()
{
    if (!_vbo_control_net)
        return;
    glDeleteBuffersARB(1, &_vbo_control_net);
    _vbo_control_net = 0;
}

GLboolean BicubicUniformPeridocBSplineSurface::UpdateVBOOfControlNet(GLenum usage_type)
{
    GLuint row = _control_net.GetRowCount();
    row = _is_closed_u_direction ? (row - 2) : row;
    GLuint col = _control_net.GetColumnCount();
    col = _is_closed_v_direction ? (col - 2) : row;

    GLuint control_point_count = row * col;

    if (!control_point_count)
        return false;

    if (usage_type != GL_STREAM_DRAW  && usage_type != GL_STREAM_READ  && usage_type != GL_STREAM_COPY &&
        usage_type != GL_STATIC_DRAW  && usage_type != GL_STATIC_READ  && usage_type != GL_STATIC_COPY &&
        usage_type != GL_DYNAMIC_DRAW && usage_type != GL_DYNAMIC_READ && usage_type != GL_DYNAMIC_COPY)
        return false;

    DeleteVBOOfControlNet();

    glGenBuffersARB(1, &_vbo_control_net);
    if (!_vbo_control_net)
        return false;

    glBindBufferARB(GL_ARRAY_BUFFER, _vbo_control_net);
    glBufferDataARB(GL_ARRAY_BUFFER, control_point_count * 6 * sizeof(GLfloat), 0, usage_type);

    GLfloat* coordinate = (GLfloat*)glMapBufferARB(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
    GLboolean asc = true;
    for (GLuint r = 0; r < row; r++)
    {
        if (asc)
        {
            for (GLuint c = 0; c < col; c++)
            {
                for (GLuint i = 0; i < 3; ++i)
                {
                    *coordinate = (GLfloat)_control_net(r, c)[i];
                    ++coordinate;
                }
            }
            asc = false;
        }
        else
        {
            for (GLuint c = col; c > 0; c--)
            {
                for (GLuint i = 0; i < 3; ++i)
                {
                    *coordinate = (GLfloat)_control_net(r, c - 1)[i];
                    ++coordinate;
                }
            }
            asc = true;
        }
    }
    asc = true;
    for (GLuint c = 0; c < col; c++)
    {
        if (asc)
        {
            for (GLuint r = 0; r < row; r++)
            {
                for (GLuint i = 0; i < 3; ++i)
                {
                    *coordinate = (GLfloat)_control_net(r, c)[i];
                    ++coordinate;
                }
            }
            asc = false;
        }
        else
        {
            for (GLuint r = row; r > 0; r--)
            {
                for (GLuint i = 0; i < 3; ++i)
                {
                    *coordinate = (GLfloat)_control_net(r - 1, c)[i];
                    ++coordinate;
                }
            }
            asc = true;
        }
    }
    if (!glUnmapBufferARB(GL_ARRAY_BUFFER))
        return false;

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::RenderControlNet(GLenum render_mode) const
{
    if (!_vbo_control_net)
        return false;

    if (render_mode != GL_LINE_STRIP && render_mode != GL_LINE_LOOP && render_mode != GL_POINTS)
        return false;

    GLuint point_count = _control_net.GetColumnCount() * _control_net.GetRowCount() * 2;

    glEnableClientState(GL_VERTEX_ARRAY);

    glBindBufferARB(GL_ARRAY_BUFFER, _vbo_control_net);
    glVertexPointer(3, GL_FLOAT, 0, (const GLvoid *)0);
    glDisable(GL_LIGHTING);
    glDrawArrays(render_mode, 0, point_count);
    glEnable(GL_LIGHTING);

    glDisableClientState(GL_VERTEX_ARRAY);
    glBindBufferARB(GL_ARRAY_BUFFER, 0);

    return true;
}

GLboolean BicubicUniformPeridocBSplineSurface::RenderControlNetOfAPatch(GLuint patch_index, GLenum render_mode) const
{
    if (patch_index >= _patches.size())
        return false;

    return _patches[patch_index].RenderControlNet(render_mode);
}

GLboolean BicubicUniformPeridocBSplineSurface::UpdateVBOOfControlNetOfAPatch(GLuint patch_index, GLenum usage_type)
{
    if (patch_index >= _patches.size())
        return false;

    return _patches[patch_index].UpdateVBOOfControlNet(usage_type);
}

BicubicUniformPeridocBSplineSurface::~BicubicUniformPeridocBSplineSurface()
{
    DeleteVBOOfControlNet();
}

GLuint BicubicUniformPeridocBSplineSurface::GetControlPointCount()
{
    return (_is_closed_v_direction ? _control_net.GetColumnCount() - 3 : _control_net.GetColumnCount())
            * (_is_closed_u_direction ? _control_net.GetRowCount() - 3 : _control_net.GetRowCount());
}
